{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "toc_visible": true,
      "authorship_tag": "ABX9TyPoaYGXMilXwlJ2bAquvWfk",
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/onlyphantom/llm-python/blob/main/workshop/Generative_AI_Template_02.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Generative AI for Financial Chatbots Development\n",
        "\n",
        "Congratulations on making it this far in this self-paced Generative AI lesson series! Before you attempt this challenge, you should complete the workbook to have a baseline understanding of the materials presented in the challenge:\n",
        "\n",
        "- [Generative AI Series 1: Generative AI for Finance](https://docs.sectors.app/recipes/generative-ai-python/01-background)\n",
        "- [Generative AI Series 2: Tool Use and Function Calling for Finance LLMs](https://docs.sectors.app/recipes/generative-ai-python/02-tool-use)\n",
        "- [Generative AI Series 3: Structured Output](https://docs.sectors.app/recipes/generative-ai-python/03-structured-output)\n",
        "- [Generative AI Series 4: Conversational Tool Use AI](https://docs.sectors.app/recipes/generative-ai-python/04-conversational)\n",
        "\n",
        "---\n",
        "\n",
        "## Generative AI Workshop\n",
        "\n",
        "The materials are specifically designed for the following workshop by Supertype, and it might be beneficial to join the workshop (\\$9, +\\$4 for certification grading, post-workshop support and API credits) for a live-instructor, hands-on experience if you're new to the topics covered.\n",
        "\n",
        "- [Generative AI for financial chatbots workshop](https://supertype.ai/financial-chatbots/)\n",
        "\n",
        "## Make a Copy for submission\n",
        "Please use File > Save a Copy in Drive to duplicate this assignment template.\n",
        "\n",
        "When you have completed the challenge, submit it to the GitHub discussion thread for grading! Good luck!\n",
        "\n",
        "---"
      ],
      "metadata": {
        "id": "pIc-fQk96x90"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Part 1: Text Extraction AI\n",
        "\n",
        "For the Challenge in this chapter, we are going to build an AI agent that can (1) extract information from unstructured\n",
        "text, (2) run validation checks on the extracted data based on schema constraints and business logic rules, and (3) generate a structured response ready\n",
        "for downstream tools to process.\n",
        "\n",
        "This has many practical applications. You can imagine an assistant chatbot that extract information from loose text such as news,\n",
        "press releases, or even user's conversational queries, and then generate structured responses to be fed into a downstream tool. One might\n",
        "also imagine a chatbot that allow user to upload a document, extract information, and then perform some actions based on the extracted data.\n",
        "\n",
        "### 5 Instructions\n",
        "There are 5 instructions in total. Each successful implementation earns you 1 point. Successfully running the following cell (`python -m pytest`) with the expectected output earns you another 1 point.\n",
        "\n",
        "The total score for Part 1 is 6 points."
      ],
      "metadata": {
        "id": "QzCggYTtOLHA"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "!pip install langchain-core\n",
        "!pip install langchain-openai\n",
        "!pip install langgraph\n",
        "!pip install langchain-groq"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "8WOjbPKiBfU6",
        "outputId": "30b5e60d-ed76-4357-8b0f-26c6c5475c8d"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Collecting langchain-core\n",
            "  Downloading langchain_core-0.3.9-py3-none-any.whl.metadata (6.3 kB)\n",
            "Requirement already satisfied: PyYAML>=5.3 in /usr/local/lib/python3.10/dist-packages (from langchain-core) (6.0.2)\n",
            "Collecting jsonpatch<2.0,>=1.33 (from langchain-core)\n",
            "  Downloading jsonpatch-1.33-py2.py3-none-any.whl.metadata (3.0 kB)\n",
            "Collecting langsmith<0.2.0,>=0.1.125 (from langchain-core)\n",
            "  Downloading langsmith-0.1.131-py3-none-any.whl.metadata (13 kB)\n",
            "Requirement already satisfied: packaging<25,>=23.2 in /usr/local/lib/python3.10/dist-packages (from langchain-core) (24.1)\n",
            "Requirement already satisfied: pydantic<3.0.0,>=2.5.2 in /usr/local/lib/python3.10/dist-packages (from langchain-core) (2.9.2)\n",
            "Collecting tenacity!=8.4.0,<9.0.0,>=8.1.0 (from langchain-core)\n",
            "  Downloading tenacity-8.5.0-py3-none-any.whl.metadata (1.2 kB)\n",
            "Requirement already satisfied: typing-extensions>=4.7 in /usr/local/lib/python3.10/dist-packages (from langchain-core) (4.12.2)\n",
            "Collecting jsonpointer>=1.9 (from jsonpatch<2.0,>=1.33->langchain-core)\n",
            "  Downloading jsonpointer-3.0.0-py2.py3-none-any.whl.metadata (2.3 kB)\n",
            "Collecting httpx<1,>=0.23.0 (from langsmith<0.2.0,>=0.1.125->langchain-core)\n",
            "  Downloading httpx-0.27.2-py3-none-any.whl.metadata (7.1 kB)\n",
            "Collecting orjson<4.0.0,>=3.9.14 (from langsmith<0.2.0,>=0.1.125->langchain-core)\n",
            "  Downloading orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (50 kB)\n",
            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m50.4/50.4 kB\u001b[0m \u001b[31m2.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hRequirement already satisfied: requests<3,>=2 in /usr/local/lib/python3.10/dist-packages (from langsmith<0.2.0,>=0.1.125->langchain-core) (2.32.3)\n",
            "Collecting requests-toolbelt<2.0.0,>=1.0.0 (from langsmith<0.2.0,>=0.1.125->langchain-core)\n",
            "  Downloading requests_toolbelt-1.0.0-py2.py3-none-any.whl.metadata (14 kB)\n",
            "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from pydantic<3.0.0,>=2.5.2->langchain-core) (0.7.0)\n",
            "Requirement already satisfied: pydantic-core==2.23.4 in /usr/local/lib/python3.10/dist-packages (from pydantic<3.0.0,>=2.5.2->langchain-core) (2.23.4)\n",
            "Requirement already satisfied: anyio in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->langsmith<0.2.0,>=0.1.125->langchain-core) (3.7.1)\n",
            "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->langsmith<0.2.0,>=0.1.125->langchain-core) (2024.8.30)\n",
            "Collecting httpcore==1.* (from httpx<1,>=0.23.0->langsmith<0.2.0,>=0.1.125->langchain-core)\n",
            "  Downloading httpcore-1.0.6-py3-none-any.whl.metadata (21 kB)\n",
            "Requirement already satisfied: idna in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->langsmith<0.2.0,>=0.1.125->langchain-core) (3.10)\n",
            "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->langsmith<0.2.0,>=0.1.125->langchain-core) (1.3.1)\n",
            "Collecting h11<0.15,>=0.13 (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<0.2.0,>=0.1.125->langchain-core)\n",
            "  Downloading h11-0.14.0-py3-none-any.whl.metadata (8.2 kB)\n",
            "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2->langsmith<0.2.0,>=0.1.125->langchain-core) (3.3.2)\n",
            "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2->langsmith<0.2.0,>=0.1.125->langchain-core) (2.2.3)\n",
            "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio->httpx<1,>=0.23.0->langsmith<0.2.0,>=0.1.125->langchain-core) (1.2.2)\n",
            "Downloading langchain_core-0.3.9-py3-none-any.whl (401 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m401.8/401.8 kB\u001b[0m \u001b[31m10.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hDownloading jsonpatch-1.33-py2.py3-none-any.whl (12 kB)\n",
            "Downloading langsmith-0.1.131-py3-none-any.whl (294 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m294.6/294.6 kB\u001b[0m \u001b[31m16.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hDownloading tenacity-8.5.0-py3-none-any.whl (28 kB)\n",
            "Downloading httpx-0.27.2-py3-none-any.whl (76 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m76.4/76.4 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hDownloading httpcore-1.0.6-py3-none-any.whl (78 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m78.0/78.0 kB\u001b[0m \u001b[31m5.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hDownloading jsonpointer-3.0.0-py2.py3-none-any.whl (7.6 kB)\n",
            "Downloading orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (141 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m141.9/141.9 kB\u001b[0m \u001b[31m10.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hDownloading requests_toolbelt-1.0.0-py2.py3-none-any.whl (54 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m54.5/54.5 kB\u001b[0m \u001b[31m3.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hDownloading h11-0.14.0-py3-none-any.whl (58 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.3/58.3 kB\u001b[0m \u001b[31m4.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hInstalling collected packages: tenacity, orjson, jsonpointer, h11, requests-toolbelt, jsonpatch, httpcore, httpx, langsmith, langchain-core\n",
            "  Attempting uninstall: tenacity\n",
            "    Found existing installation: tenacity 9.0.0\n",
            "    Uninstalling tenacity-9.0.0:\n",
            "      Successfully uninstalled tenacity-9.0.0\n",
            "Successfully installed h11-0.14.0 httpcore-1.0.6 httpx-0.27.2 jsonpatch-1.33 jsonpointer-3.0.0 langchain-core-0.3.9 langsmith-0.1.131 orjson-3.10.7 requests-toolbelt-1.0.0 tenacity-8.5.0\n",
            "Collecting langchain-openai\n",
            "  Downloading langchain_openai-0.2.2-py3-none-any.whl.metadata (2.6 kB)\n",
            "Requirement already satisfied: langchain-core<0.4.0,>=0.3.9 in /usr/local/lib/python3.10/dist-packages (from langchain-openai) (0.3.9)\n",
            "Collecting openai<2.0.0,>=1.40.0 (from langchain-openai)\n",
            "  Downloading openai-1.51.0-py3-none-any.whl.metadata (24 kB)\n",
            "Collecting tiktoken<1,>=0.7 (from langchain-openai)\n",
            "  Downloading tiktoken-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.6 kB)\n",
            "Requirement already satisfied: PyYAML>=5.3 in /usr/local/lib/python3.10/dist-packages (from langchain-core<0.4.0,>=0.3.9->langchain-openai) (6.0.2)\n",
            "Requirement already satisfied: jsonpatch<2.0,>=1.33 in /usr/local/lib/python3.10/dist-packages (from langchain-core<0.4.0,>=0.3.9->langchain-openai) (1.33)\n",
            "Requirement already satisfied: langsmith<0.2.0,>=0.1.125 in /usr/local/lib/python3.10/dist-packages (from langchain-core<0.4.0,>=0.3.9->langchain-openai) (0.1.131)\n",
            "Requirement already satisfied: packaging<25,>=23.2 in /usr/local/lib/python3.10/dist-packages (from langchain-core<0.4.0,>=0.3.9->langchain-openai) (24.1)\n",
            "Requirement already satisfied: pydantic<3.0.0,>=2.5.2 in /usr/local/lib/python3.10/dist-packages (from langchain-core<0.4.0,>=0.3.9->langchain-openai) (2.9.2)\n",
            "Requirement already satisfied: tenacity!=8.4.0,<9.0.0,>=8.1.0 in /usr/local/lib/python3.10/dist-packages (from langchain-core<0.4.0,>=0.3.9->langchain-openai) (8.5.0)\n",
            "Requirement already satisfied: typing-extensions>=4.7 in /usr/local/lib/python3.10/dist-packages (from langchain-core<0.4.0,>=0.3.9->langchain-openai) (4.12.2)\n",
            "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from openai<2.0.0,>=1.40.0->langchain-openai) (3.7.1)\n",
            "Requirement already satisfied: distro<2,>=1.7.0 in /usr/lib/python3/dist-packages (from openai<2.0.0,>=1.40.0->langchain-openai) (1.7.0)\n",
            "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.10/dist-packages (from openai<2.0.0,>=1.40.0->langchain-openai) (0.27.2)\n",
            "Collecting jiter<1,>=0.4.0 (from openai<2.0.0,>=1.40.0->langchain-openai)\n",
            "  Downloading jiter-0.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (3.6 kB)\n",
            "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from openai<2.0.0,>=1.40.0->langchain-openai) (1.3.1)\n",
            "Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.10/dist-packages (from openai<2.0.0,>=1.40.0->langchain-openai) (4.66.5)\n",
            "Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.10/dist-packages (from tiktoken<1,>=0.7->langchain-openai) (2024.9.11)\n",
            "Requirement already satisfied: requests>=2.26.0 in /usr/local/lib/python3.10/dist-packages (from tiktoken<1,>=0.7->langchain-openai) (2.32.3)\n",
            "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai<2.0.0,>=1.40.0->langchain-openai) (3.10)\n",
            "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai<2.0.0,>=1.40.0->langchain-openai) (1.2.2)\n",
            "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->openai<2.0.0,>=1.40.0->langchain-openai) (2024.8.30)\n",
            "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->openai<2.0.0,>=1.40.0->langchain-openai) (1.0.6)\n",
            "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai<2.0.0,>=1.40.0->langchain-openai) (0.14.0)\n",
            "Requirement already satisfied: jsonpointer>=1.9 in /usr/local/lib/python3.10/dist-packages (from jsonpatch<2.0,>=1.33->langchain-core<0.4.0,>=0.3.9->langchain-openai) (3.0.0)\n",
            "Requirement already satisfied: orjson<4.0.0,>=3.9.14 in /usr/local/lib/python3.10/dist-packages (from langsmith<0.2.0,>=0.1.125->langchain-core<0.4.0,>=0.3.9->langchain-openai) (3.10.7)\n",
            "Requirement already satisfied: requests-toolbelt<2.0.0,>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from langsmith<0.2.0,>=0.1.125->langchain-core<0.4.0,>=0.3.9->langchain-openai) (1.0.0)\n",
            "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from pydantic<3.0.0,>=2.5.2->langchain-core<0.4.0,>=0.3.9->langchain-openai) (0.7.0)\n",
            "Requirement already satisfied: pydantic-core==2.23.4 in /usr/local/lib/python3.10/dist-packages (from pydantic<3.0.0,>=2.5.2->langchain-core<0.4.0,>=0.3.9->langchain-openai) (2.23.4)\n",
            "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests>=2.26.0->tiktoken<1,>=0.7->langchain-openai) (3.3.2)\n",
            "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.26.0->tiktoken<1,>=0.7->langchain-openai) (2.2.3)\n",
            "Downloading langchain_openai-0.2.2-py3-none-any.whl (49 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.7/49.7 kB\u001b[0m \u001b[31m2.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hDownloading openai-1.51.0-py3-none-any.whl (383 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m383.5/383.5 kB\u001b[0m \u001b[31m11.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hDownloading tiktoken-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.2 MB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.2/1.2 MB\u001b[0m \u001b[31m39.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hDownloading jiter-0.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (318 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m318.9/318.9 kB\u001b[0m \u001b[31m12.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hInstalling collected packages: jiter, tiktoken, openai, langchain-openai\n",
            "Successfully installed jiter-0.5.0 langchain-openai-0.2.2 openai-1.51.0 tiktoken-0.8.0\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "W7LpQdAG6beJ",
        "outputId": "c117517b-ce94-46dc-b9d8-225bad5c3b07"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Overwriting test_parser.py\n"
          ]
        }
      ],
      "source": [
        "%%file test_parser.py\n",
        "\n",
        "from typing import Optional\n",
        "import pytest\n",
        "\n",
        "from pydantic import BaseModel, Field, model_validator\n",
        "\n",
        "from langchain_core.output_parsers import PydanticOutputParser\n",
        "from langchain_core.prompts import PromptTemplate\n",
        "\n",
        "from langchain_core.exceptions import OutputParserException\n",
        "\n",
        "# 1. bring in your llm\n",
        "# llm = ...\n",
        "\n",
        "\n",
        "class Stock(BaseModel):\n",
        "    \"\"\"Information about a company's stock\"\"\"\n",
        "\n",
        "    symbol: str = Field(description=\"The stock symbol\")\n",
        "    name: str = Field(\n",
        "        description=\"The name of the company for which the stock symbol represents\"\n",
        "    )\n",
        "    sector: Optional[str] = Field(default=None, description=\"The sector of the company\")\n",
        "    # 2. implement the other fields\n",
        "    # ...\n",
        "\n",
        "    @model_validator(mode=\"before\")\n",
        "    @classmethod\n",
        "    def validate_symbol_4_letters(cls, values: dict) -> dict:\n",
        "        # 3. implement LLM validation logic\n",
        "        # ...\n",
        "        pass\n",
        "\n",
        "parser = PydanticOutputParser(pydantic_object=Stock)\n",
        "\n",
        "prompt = PromptTemplate(\n",
        "    template=\"Answer the user query.\\n{format_instructions}\\n{query}\\n\",\n",
        "    input_variables=[\"query\"],\n",
        "    partial_variables={\"format_instructions\": parser.get_format_instructions()},\n",
        ")\n",
        "\n",
        "runnable = prompt | llm | parser\n",
        "\n",
        "\n",
        "class TestParser:\n",
        "    def test_output_parser_symbol_valid(self):\n",
        "        text = \"\"\"\n",
        "        Bank Central Asia (BBCA) is a bank in Indonesia and is part of the finance sector.\n",
        "            It is in the banking industry and has a market capitalization of $8.5 billion.\n",
        "        \"\"\"\n",
        "        # 4. implement when symbol and market cap (and other fields) are all valid\n",
        "        ...\n",
        "\n",
        "\n",
        "    def test_output_parser_symbol_invalid(self):\n",
        "        text = \"\"\"\n",
        "        Bank Central Asia (BCA) is a bank in Indonesia and is part of the finance sector.\n",
        "            It is in the banking industry and has a market capitalization of $8.5 billion.\n",
        "        \"\"\"\n",
        "\n",
        "        # assert exception is raised when the symbol is not 4 letters long\n",
        "        with pytest.raises(OutputParserException):\n",
        "            out = runnable.invoke(text)\n",
        "\n",
        "    def test_output_parser_mcap_invalid(self):\n",
        "        text = \"\"\"\n",
        "        Bank Central Asia (BBCA) is a bank in Indonesia and is part of the finance sector.\n",
        "            It is in the banking industry and has a market capitalization of $-8.5 billion.\n",
        "        \"\"\"\n",
        "\n",
        "        # 5. assert exception is raised when extraction task fail by detecting <0 market cap\n",
        "        # ...\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import os\n",
        "from google.colab import userdata\n",
        "\n",
        "os.environ[\"OPENAI_API_KEY\"] = userdata.get('OPENAI_API_KEY')\n",
        "\n",
        "# 6. run this with 3 passes\n",
        "!python -m pytest test_parser.py"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "9OkGEO2mBynI",
        "outputId": "f892bbd1-3760-4e2a-9ff8-3a05fefcf9b1"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\u001b[1m======================================= test session starts ========================================\u001b[0m\n",
            "platform linux -- Python 3.10.12, pytest-7.4.4, pluggy-1.5.0\n",
            "rootdir: /content\n",
            "plugins: anyio-3.7.1, typeguard-4.3.0\n",
            "collected 3 items                                                                                  \u001b[0m\n",
            "\n",
            "test_parser.py \u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m                                                                           [100%]\u001b[0m\n",
            "\n",
            "\u001b[32m======================================== \u001b[32m\u001b[1m3 passed\u001b[0m\u001b[32m in 4.90s\u001b[0m\u001b[32m =========================================\u001b[0m\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "- Do not alter any of the `text` prompt. Doing so invalidatest the purpose of the quiz / challenge.\n",
        "- Each correct implementation gets you 1 point. Successfully executing the cell above (`python -m pytest test_parser.py`) with the expected output gets you another 1 point. You get a total of 5+1 points from this section above.  "
      ],
      "metadata": {
        "id": "q-OdgLRmNHf6"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Part 2: A LangGraph ReAct Agent with retriever tools"
      ],
      "metadata": {
        "id": "dF20ZU6qQZBB"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import json\n",
        "import requests\n",
        "from typing import List\n",
        "\n",
        "from langchain_core.tools import tool\n",
        "from langgraph.prebuilt import create_react_agent\n",
        "from langchain_groq import ChatGroq\n",
        "from langchain_core.messages import HumanMessage\n",
        "\n",
        "\n",
        "SECTORS_API_KEY = userdata.get('SECTORS_API_KEY')\n",
        "GROQ_API_KEY = userdata.get('GROQ_API_KEY')\n",
        "\n",
        "def retrieve_from_endpoint(url: str) -> dict:\n",
        "    headers = {\"Authorization\": SECTORS_API_KEY}\n",
        "\n",
        "    try:\n",
        "        response = requests.get(url, headers=headers)\n",
        "        response.raise_for_status()\n",
        "        data = response.json()\n",
        "    except requests.exceptions.HTTPError as err:\n",
        "        raise SystemExit(err)\n",
        "    return json.dumps(data)\n",
        "\n",
        "\n",
        "@tool\n",
        "def get_company_overview(stock: str) -> str:\n",
        "    \"\"\"\n",
        "    Get company overview\n",
        "\n",
        "    @param stock: The stock symbol of the company\n",
        "    @return: The company overview\n",
        "    \"\"\"\n",
        "\n",
        "    url = f\"https://api.sectors.app/v1/company/report/{stock}/?sections=overview\"\n",
        "    return retrieve_from_endpoint(url)\n",
        "\n",
        "@tool\n",
        "def get_top_companies_ranked(dimension: str) -> List[str]:\n",
        "   # 7. implement this tool correctly, using the tool implementation above as reference\n",
        "   pass\n",
        "\n",
        "\n",
        "llm = ChatGroq(\n",
        "    temperature=0,\n",
        "    model_name=\"llama3-groq-70b-8192-tool-use-preview\",\n",
        "    groq_api_key=GROQ_API_KEY,\n",
        ")\n",
        "\n",
        "tools = [\n",
        "    get_company_overview,\n",
        "    get_top_companies_ranked,\n",
        "]\n",
        "\n",
        "# 8: ask that floating numbers are returned in 2 decimal points so the result is prettier\n",
        "# return full company name, symbol, and the value (in the case of companies by p/e values, return the p/e\n",
        "# but in 2 decimal points)\n",
        "system_message = \"\"\n",
        "\n",
        "\n",
        "# 9: implement the below correctly, with llm, tools, and system_message as state modifier\n",
        "app = create_react_agent()\n",
        "\n",
        "def query_app(text: str) -> str:\n",
        "    out = app.invoke(\n",
        "        {\n",
        "            \"messages\": [\n",
        "                HumanMessage(text),\n",
        "            ]\n",
        "        }\n",
        "    )\n",
        "    # return out[\"messages\"][-1].content\n",
        "    return out[\"messages\"]\n",
        "\n",
        "out_agent = query_app(\n",
        "    \"Get me the top 7 companies based on P/E values, along with their full company name and PE values\"\n",
        ")\n",
        "\n",
        "print(out_agent[-1].content)\n"
      ],
      "metadata": {
        "id": "8O6pqWWdQXKp"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# 10: follow up now with a second question, to get the overview of whichever symbol\n",
        "# is 4th on the list above in `out_agent`\n",
        "\n",
        "out_agent2 = ...\n",
        "\n",
        "print(out_agent2[-1].content)"
      ],
      "metadata": {
        "id": "FQCjhNK8lpEl"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Conclusion\n",
        "\n",
        "Congratulations on making your way through the challenges. My hope is that you find the session educational and fun, and I have, in my own way, inspired you to dive deeper into the exciting world of building financial chat agents using information retriever tools!\n",
        "\n",
        "Please submit your work at the GitHub repository discussion thread for grading. If you score 8/10 you will obtain a certification jointly issued by Supertype and Sectors.\n",
        "\n",
        "If you need help, please reach out to us on Discord (exclusively for Practicum members).\n",
        "\n",
        "Thank you again for your participation, and I hope you walked away with lots of new ideas on what to build next!"
      ],
      "metadata": {
        "id": "wwzLbPLfl-G2"
      }
    }
  ]
}